<?php

	require_once "googledocs/GoogleDocsLoader.php";
	require_once "publisher/GoogleDocsPublisher.php";
	require_once "utils/PathUtil.php";

	/**
	 * Publish and package documents and dependencies.
	 */
	class Publisher {

		private $googleUser;
		private $googlePass;
		private $files;
		private $googleDocs;
		private $googleDocsLoader;
		private $title;
		private $description;
		private $swf;

		/**
		 * Constructor.
		 */
		public function Publisher() {
			$this->files=array();
			$this->googleDocs=array();
			$this->googleDocsLoader=new GoogleDocsLoader();
			$this->title="My Course";
			$this->description="No description available";
		}

		/**
		 * Set title.
		 */
		public function setTitle($value) {
			$this->title=$value;
		}

		/**
		 * Set description.
		 */
		public function setDescription($value) {
			$this->description=$value;
		}

		/**
		 * Set main swf to use for publication.
		 */
		public function setSwf($value) {
			$this->swf=$value;
		}

		/**
		 * Set google credentials.
		 */
		public function setGoogleCredentials($username, $password) {
			$this->googleUser=$username;
			$this->googlePass=$password;
		}

		/**
		 * Add a google doc dependency.
		 */
		public function addGoogleDoc($target, $docId, $template) {
			$doc=new GoogleDocsPublisher();
			$doc->setPublisher($this);
			$doc->setDocumentId($docId);
			$doc->setTemplate($template);
			$doc->setTarget($target);

			$this->googleDocs[]=$doc;
		}

		/**
		 * Add a file dependency.
		 */
		public function addFile($fn) {
			$this->files[]=$fn;
		}

		/**
		 * Generate google docs dependencies.
		 */
		private function generateDependencies() {
			$this->message("Generating dependencies...");

			try {
				$this->googleDocsLoader->login($this->googleUser,$this->googlePass);
				foreach ($this->googleDocs as $googleDoc)
					$googleDoc->run();

				$usedImages=array();
				foreach ($this->googleDocs as $googleDoc)
					$usedImages=array_merge($googleDoc->getUsedImages());

				$this->message("Writing: usedfiles.txt");
				$res=file_put_contents(PathUtil::getScriptDir()."/"."usedfiles.txt", join("\n",$usedImages));

				if (!$res)
					throw new Exception("Unable to write usedfiles.txt");

				$this->message("Done!");
			}

			catch (Exception $e) {
				$this->exceptionFail($e);
			}
		}

		/**
		 * Fail due to exception.
		 */
		private function exceptionFail($e) {
			$this->message("Build failed...");
			$this->message("");
			$this->message("*************************************************************************");
			$this->message("*");
			$this->message("*  ".$e->getMessage());
			$this->message("*");
			$this->message("*************************************************************************");
			$this->message("");
			$this->message($e->getTraceAsString());
			exit();
		}

		/**
		 * Get short name.
		 */
		private function getShortName() {
			return str_replace(" ","",$this->title);
		}

		/**
		 * Generate a scorm package.
		 */
		private function generateScorm() {
			try {
				$allFiles=array();

				if (!$this->swf)
					$this->fail("Need a swf file for scorm publication.");

				$this->message("Generating scorm package...");

				$zip=new ZipArchive();
				$zip->open(PathUtil::getScriptDir()."/".$this->getScormName(),ZIPARCHIVE::OVERWRITE);

				$this->message("Adding main swf: ".$this->swf);
				$zip->addFile(PathUtil::getScriptDir()."/".$this->swf,$this->swf);
				$allFiles[]=$this->swf;

				foreach ($this->files as $file) {
					$this->message("Adding file: ".$file);
					$zip->addFile(PathUtil::getScriptDir()."/".$file,$file);
					$allFiles[]=$file;
				}

				foreach ($this->googleDocs as $googleDoc) {
					$this->message("Adding generated document: ".$googleDoc->getTarget());
					$zip->addFile(PathUtil::getScriptDir()."/".$googleDoc->getTarget(),$googleDoc->getTarget());
					$allFiles[]=$googleDoc->getTarget();
				}

				$usedfiles=file_get_contents(PathUtil::getScriptDir()."/usedfiles.txt");
				foreach (explode("\n",$usedfiles) as $usedfile) {
					$this->message("Addig document dependency: ".$usedfile);
					$zip->addFile(PathUtil::getScriptDir()."/".$usedfile,$usedfile);
					$allFiles[]=$usedfile;
				}

				foreach (scandir(dirname(__FILE__)."/scormfiles") as $file) {
					if ($file[0]!=".") {
						$this->message("Adding scorm file: ".$file);
						$zip->addFile(dirname(__FILE__)."/scormfiles/".$file,$file);
						$allFiles[]=$file;
					}
				}

				$replacements=array(
					"@@TITLE@@"=>$this->title,
					"@@SWF@@"=>$this->swf,
					"@@DESCRIPTION@@"=>$this->description,
					"@@RESOURCELIST@@"=>""
				);

				foreach ($allFiles as $file)
					$replacements["@@RESOURCELIST@@"].="<file href=\"$file\"/>";

				foreach (scandir(dirname(__FILE__)."/scormtemplates") as $file) {
					if ($file[0]!=".") {
						$this->message("Generating: ".$file);
						$template=file_get_contents(dirname(__FILE__)."/scormtemplates/".$file);

						foreach ($replacements as $key=>$value)
							$template=str_replace($key, $value, $template);

						$zip->addFromString($file,$template);
					}
				}

				$this->message("Generating scorm: ".$this->getScormName());
				$zip->close();

				$this->message("Done!");
			}

			catch (Exception $e) {
				$this->exceptionFail($e);
			}
		}

		/**
		 * Get scorm name.
		 */
		public function getScormName() {
			return $this->getShortName().".zip";
		}

		/**
		 * Get google docs loader.
		 */
		public function getGoogleDocsLoader() {
			return $this->googleDocsLoader;
		}

		/**
		 * Print a message.
		 */
		public function message($message) {
			if ($this->isCommandLine())
				echo $message."\n";

			else {
				foreach (explode("\n", $message) as $m)
					echo '<script type="text/javascript">document.getElementById("log").value+="'.addslashes($m).'\n";</script>';
			}
		}

		/**
		 * Are we running from the command line?
		 */
		private function isCommandLine() {
			return php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']);
		}

		/**
		 * Show usage and exit.
		 */
		private function usage() {
			echo "Command line usage: publish.php <action>\n";
			echo "Available actions:\n";
			echo "  deps+scorm - Create dependencies and scorm package.\n";
			echo "  deps - Create dependencies.\n";
			echo "  scorm - Create scorm package.\n";
			echo "  clean - Remove generated files.\n";
			exit();
		}

		/**
		 * Fail.
		 */
		public function fail($message) {
			if ($this->isCommandLine()) {
				echo $message."\n";
				exit();
			}
		}

		/**
		 * Remove generated files.
		 */
		private function clean() {
			$this->message("Removing build...");

			foreach ($this->googleDocs as $googleDoc) {
				$this->message("Removing: ".$googleDoc->getTarget());
				@unlink(PathUtil::getScriptDir()."/".$googleDoc->getTarget());
			}

			$this->message("Rmoving: ".$this->getScormName());
			@unlink(PathUtil::getScriptDir()."/".$this->getScormName());

			if (file_exists(PathUtil::getScriptDir()."/usedfiles.txt")) {
				$usedfiles=file_get_contents(PathUtil::getScriptDir()."/usedfiles.txt");
				foreach (explode("\n",$usedfiles) as $usedfile) {
					$this->message("Removing: ".$usedfile);
					@unlink(PathUtil::getScriptDir()."/".$usedfile);
				}

				$this->message("Rmoving: usedfiles.txt");
				@unlink(PathUtil::getScriptDir()."/usedfiles.txt");
			}

			$this->message("Done!");
		}

		/**
		 * Format filesize for human reading.
		 */
		function humanSize($bytes, $decimals = 2) {
			$sz = 'BKMGTP';
			$factor = floor((strlen($bytes) - 1) / 3);
			return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
		}

		/**
		 * Show main page.
		 */
		private function showMain() {
			$page=array();
			$page["title"]=$this->title;

			if (file_exists(PathUtil::getScriptDir()."/".$this->getScormName())) {
				$mtime=filemtime(PathUtil::getScriptDir()."/".$this->getScormName());
				$page["buildinfo"]="Last build: ".date("l M d, H:i:s",$mtime);
				$page["scormfile"]=$this->getScormName();
				$page["scormsize"]=$this->humanSize(filesize(PathUtil::getScriptDir()."/".$this->getScormName()),1);
			}

			else {
				$page["buildinfo"]="No current build.";
			}

			require dirname(__FILE__)."/wwwtemplates/main.tpl.php";
		}

		/**
		 * Dispatch.
		 */
		public function dispatch() {
			if ($this->isCommandLine()) {
				if (sizeof($_SERVER["argv"])!=2)
					$this->usage();

				switch ($_SERVER["argv"][1]) {
					case "deps+scorm":
						$this->generateDependencies();
						$this->generateScorm();
						break;

					case "deps":
						$this->generateDependencies();
						break;

					case "scorm":
						$this->generateScorm();
						break;

					case "clean":
						$this->clean();
						break;

					default:
						$this->usage();
						break;
				}
			}

			else {
				if (array_key_exists("file", $_REQUEST)) {
					$file=$_REQUEST["file"];
					$file=str_replace("/", "", $file);
					readfile(dirname(__FILE__)."/wwwfiles/".$file);
				}

				else {
					if (array_key_exists("action",$_REQUEST))
						$action=$_REQUEST["action"];

					else
						$action="main";

					switch ($action) {
						case "preview":
							$page=array();
							$page["title"]=$this->title;
							require dirname(__FILE__)."/wwwtemplates/preview.tpl.php";
							break;

						case "main":
							$this->showMain();
							$this->message("(no log at this time)");
							break;

						case "swf":
							readfile(PathUtil::getScriptDir()."/".$this->swf);
							break;

						case "download":
							header('Content-Disposition: attachment; filename="'.$this->getScormName().'"');
							readfile(PathUtil::getScriptDir()."/".$this->getScormName());
							break;

						case "build":
							$this->showMain();
							@apache_setenv('no-gzip', 1);
							@ini_set('zlib.output_compression', 0);
							@ini_set('implicit_flush', 1);

							$levels=ob_get_level();
							for ($i=0; $i<$levels; $i++) {
								ob_end_flush();
							}

							ob_implicit_flush(TRUE);
							$this->generateDependencies();
							$this->generateScorm();
							echo '<script type="text/javascript">window.location.href="?action=main";</script>';
							break;

						default:
							throw new Exception("Unknown action: ".$action);
							break;
					}
				}
			}
		}
	}